home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 17 / CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso / CUCD / Programming / DiceSource / lib / extra / system13.c < prev    next >
C/C++ Source or Header  |  1997-09-09  |  13KB  |  594 lines

  1.  
  2. /*
  3.  *  SYSTEM13.C
  4.  *
  5.  *    (c)Copyright 1992-1997 Obvious Implementations Corp.  Redistribution and
  6.  *    use is allowed under the terms of the DICE-LICENSE FILE,
  7.  *    DICE-LICENSE.TXT.
  8.  *
  9.  *  This routine will do the equivalent of system() for programs running
  10.  *  under the 1.3 operating system.  system13() will work from either a
  11.  *  CLI or workbench-run program and will properly return the exit code
  12.  *  of the program.
  13.  *
  14.  *  Due to overhead, this call has not been integrated into system()
  15.  *
  16.  *  Under 1.3, NEWCLI, ENDCLI, and RUN must be in your path OR made
  17.  *  resident.  Note that making these three programs resident will
  18.  *  greatly increase system13()'s efficiency.  The dummy shell is left
  19.  *  open for the duration of the program, so multiple system() calls
  20.  *  will be efficient.
  21.  *
  22.  *    D0 - scratch
  23.  *    D1 - scratch
  24.  *
  25.  *    A0 - scratch
  26.  *    A1 - server is_Data pointer (scratch)
  27.  *
  28.  *    A5 - jump vector register (scratch)
  29.  *    A6 - scratch
  30.  *
  31.  */
  32.  
  33. #define DOSBase_DECLARED
  34.  
  35. #include <exec/types.h>
  36. #include <exec/nodes.h>
  37. #include <exec/ports.h>
  38. #include <exec/interrupts.h>
  39. #include <exec/memory.h>
  40. #include <exec/alerts.h>
  41. #include <dos/dos.h>
  42. #include <dos/dosextens.h>
  43. #include <dos/filehandler.h>
  44. #include <clib/dos_protos.h>
  45. #include <clib/exec_protos.h>
  46. #include <clib/alib_protos.h>
  47. #include <stdio.h>
  48. #include <stdarg.h>
  49. #include <stdlib.h>
  50. #include <string.h>
  51. #include <lib/misc.h>
  52.  
  53. #define BTOC(bptr)  ((void *)((long)(bptr) << 2))
  54. #define CTOB(cptr)  ((BPTR)(((long)cptr) >> 2))
  55.  
  56. #define DOS_TRUE    (-1)
  57. #define DOS_FALSE   (0)
  58.  
  59. #ifdef DEBUG
  60. #define dbprintf(x) fhprintf x
  61. #else
  62. #define dbprintf(x)
  63. #endif
  64.  
  65. typedef struct DosPacket    DosPacket;
  66. typedef struct FileHandle   FileHandle;
  67. typedef struct DeviceNode   DeviceNode;
  68. typedef struct Process        Process;
  69. typedef struct MinNode        Node;
  70. typedef struct List        List;
  71. typedef struct Node        MaxNode;
  72. typedef struct MsgPort        MsgPort;
  73. typedef struct Message        Message;
  74. typedef struct Interrupt    Interrupt;
  75. typedef struct DosList        DosList;
  76. typedef struct RootNode     RootNode;
  77. typedef struct DosInfo        DosInfo;
  78. typedef struct CommandLineInterface CLI;
  79. typedef struct Task        Task;
  80.  
  81. typedef struct LockList {
  82.     BPTR    NextPath;
  83.     BPTR    PathLock;
  84. } LockList;
  85.  
  86.  
  87. void *DosAllocMem(long);
  88. void DosFree(void *);
  89. void MkDevice(char *, MsgPort *);
  90. void DelDevice(void);
  91. void ReturnPacket(DosPacket *);
  92. void CopyWorkbenchPath(CLI *);
  93. long StartCommand(char *);
  94. long WaitCommand(void);
  95. void WaitPktMask(void);
  96.  
  97. long _sys13SoftIntC(void);
  98. long _sys13SoftIntA(__A1 void *);
  99.  
  100. extern struct DosLibrary *DOSBase;
  101.  
  102. static DosList *Dl;
  103. static List    ReqList;    /*  pending read requests on dummy handle */
  104. static MsgPort    PktPort;
  105. static Task    *RemShellTask;
  106. static char    DevName[16];
  107. static char    DevBuf[128];
  108. static long    DummyRefs;
  109. static long    PktMask;
  110. static long    ReturnCode;
  111. static short    CmdStatus;    /*  0 none running, 1 in prog, -1 done    */
  112. static short    ShellRunning;
  113. static short    ConsoleTaken;
  114. static short    TermFlag;
  115. static long    BreakMask;
  116. static Interrupt DevInt;
  117.  
  118. #ifdef DEBUG
  119. static BPTR DBFh;
  120. #endif
  121.  
  122. static __autoinit
  123. void
  124. sys13_startup()
  125. {
  126.     struct Process *proc = (struct Process *)FindTask(NULL);
  127.     sprintf(DevName, "DICE%08lx", &TermFlag);
  128.  
  129. #ifdef DEBUG
  130.     DBFh = Open("con:0/0/110/100/debug", 1005);
  131. #endif
  132.  
  133.     dbprintf((DBFh, "PROCESS %08lx\n", proc));
  134.  
  135.     NewList(&ReqList);
  136.     PktPort.mp_Node.ln_Name = (char *)&proc->pr_Task;
  137.     PktPort.mp_Node.ln_Type = NT_MSGPORT;
  138.     PktPort.mp_Flags = PA_SOFTINT;
  139.     PktPort.mp_SigBit = AllocSignal(-1);
  140.     PktPort.mp_SoftInt = &DevInt;
  141.  
  142.     NewList(&PktPort.mp_MsgList);
  143.  
  144.     DevInt.is_Data = (APTR)rega4();
  145.     DevInt.is_Code = (void(*)())_sys13SoftIntA;
  146.     DevInt.is_Node.ln_Type = NT_INTERRUPT;
  147.  
  148.     PktMask = 1 << PktPort.mp_SigBit;
  149.  
  150.     MkDevice(DevName, &PktPort);
  151.  
  152.     dbprintf((DBFh, "TEST1\n"));
  153. }
  154.  
  155. static __autoexit
  156. void
  157. sys13_shutdown()
  158. {
  159.     struct Process *proc = (struct Process *)FindTask(NULL);
  160.  
  161.     if (ShellRunning) {
  162.     TermFlag = 1;
  163.     if (StartCommand("endcli") == 0)
  164.         WaitCommand();
  165.     while (DummyRefs)
  166.         WaitPktMask();
  167.     }
  168.     DelDevice();
  169.     if ((char)PktPort.mp_SigBit >= 0) {
  170.     FreeSignal(PktPort.mp_SigBit);
  171.     PktPort.mp_SigBit = -1;
  172.     }
  173. #ifdef DEBUG
  174.     if (DBFh) {
  175.     Close(DBFh);
  176.     DBFh = 0;
  177.     }
  178. #endif
  179. }
  180.  
  181. /*
  182.  *  Are we having fun yet?  Now, we really roll...
  183.  *
  184.  *  (1) Open a dummy file handle (may already be done)
  185.  *  (2) Execute("", fh, NULL) to get a remote shell (may already be done)
  186.  *  (3) Supply command as a replied read request
  187.  *  (4) wait until cli_Module becomes NULL (command complete)
  188.  *  (5) Obtain return code
  189.  *  (6) ENDCLI the shell (defered until autoexit)
  190.  */
  191.  
  192. int
  193. system13(buf)
  194. const char *buf;
  195. {
  196.     long r = -1;
  197.  
  198.     if (StartCommand(buf) == 0)
  199.     r = WaitCommand();
  200.     return(r);
  201. }
  202.  
  203. /*
  204.  *  WARNING!!! We cannot make *any* DOS calls while a command is in
  205.  *  progress due to the possibility of an exception interrupting us
  206.  *  in the middle of such a call, then trying to do a DOS call itself.
  207.  */
  208.  
  209. static long
  210. StartCommand(buf)
  211. char *buf;
  212. {
  213.     Process *proc = (Process *)PktPort.mp_Node.ln_Name;
  214.  
  215.     dbprintf((DBFh, "START1\n"));
  216.  
  217.     if (proc->pr_ConsoleTask == NULL) {
  218.     proc->pr_ConsoleTask = &PktPort;
  219.     ConsoleTaken = 1;
  220.     }
  221.  
  222.     if (ShellRunning == 0) {
  223.     /*
  224.      *  can't do this in autoinit or _main will think we have a
  225.      *  real console and attempt to open it!
  226.      */
  227.  
  228.     TermFlag = 1;
  229.     sprintf(DevBuf, "newshell <%s: >%s: %s: from nil:", DevName, DevName, DevName);
  230.     Execute(DevBuf, 0, 0);
  231.     /*System(DevBuf, NULL);*/
  232.     if (IoErr()) {
  233.         if (ConsoleTaken) {
  234.         proc->pr_ConsoleTask = NULL;
  235.         ConsoleTaken = 0;
  236.         }
  237.         TermFlag = 0;
  238.         return(-1);
  239.     }
  240.     ShellRunning = 1;
  241.     if (StartCommand("") == 0)  /*  synchronize */
  242.         WaitCommand();
  243.     TermFlag = 0;
  244.  
  245.     /*
  246.      *  If running from the workbench we have no CLI and must copy
  247.      *  the path from the workbench process's CLI
  248.      */
  249.  
  250.     if ((((Task *)PktPort.mp_Node.ln_Name)->tc_Node.ln_Type != NT_PROCESS || ((Process *)PktPort.mp_Node.ln_Name)->pr_CLI == NULL) && RemShellTask) {
  251.         if (RemShellTask->tc_Node.ln_Type == NT_PROCESS && ((Process *)RemShellTask)->pr_CLI)
  252.         CopyWorkbenchPath(BTOC(((Process *)RemShellTask)->pr_CLI));
  253.     }
  254.     }
  255.  
  256.     dbprintf((DBFh, "START2\n"));
  257.  
  258.     /*
  259.      *    Preset cli_ReturnCode in case command not found
  260.      */
  261.  
  262.     if (RemShellTask) {
  263.     CLI *cli = BTOC(((Process *)RemShellTask)->pr_CLI);
  264.     if (cli)
  265.         cli->cli_ReturnCode = 10;
  266.     }
  267.  
  268.     /*
  269.      *    Supply command line by replying to read requests.  Turn off
  270.      *    signal exceptions to handle it synchronously
  271.      */
  272.  
  273.     {
  274.     Message *msg;
  275.     DosPacket *pkt;
  276.     short len = strlen(buf);
  277.     short n;
  278.  
  279.     if (len == 0 || buf[len-1] != '\n')
  280.         ++len;  /*    virtual newline */
  281.  
  282.     while (len) {
  283.         for (;;) {
  284.         Disable();
  285.         msg = (Message *)RemHead(&ReqList);
  286.         Enable();
  287.         if (msg)
  288.             break;
  289.         WaitPktMask();
  290.         }
  291.         pkt = (DosPacket *)msg->mn_Node.ln_Name;
  292.  
  293.         if (TermFlag == 0 && pkt->dp_Port->mp_SigTask != (Task *)PktPort.mp_Node.ln_Name) {
  294.         if ((pkt->dp_Port->mp_Flags & PF_ACTION) == PA_SIGNAL && ((Task *)pkt->dp_Port->mp_SigTask)->tc_Node.ln_Type == NT_PROCESS)
  295.             RemShellTask = pkt->dp_Port->mp_SigTask;
  296.         }
  297.  
  298.         /*
  299.          *    Handle packet
  300.          */
  301.  
  302.         if ((n = pkt->dp_Arg3) > len)
  303.         n = len;
  304.         movmem(buf, (void *)pkt->dp_Arg2, n);
  305.         if (len == n)   /*    last char is a newline    */
  306.         ((char *)pkt->dp_Arg2)[n-1] = '\n';
  307.         len -= n;
  308.         buf += n;
  309.         pkt->dp_Res1 = n;
  310.         CmdStatus = 1;    /*  in progress */
  311.         ReturnPacket(pkt);
  312.     }
  313.     }
  314.     return(0);
  315. }
  316.  
  317. /*
  318.  *  Wait for command completion - last handle closed or cli_Module == NULL
  319.  *
  320.  *  Reenable signal exceptions after completion
  321.  */
  322.  
  323. static long
  324. WaitCommand()
  325. {
  326.     Process *proc = (Process *)PktPort.mp_Node.ln_Name;
  327.  
  328.     dbprintf((DBFh, "WAIT1\n"));
  329.  
  330.     while (CmdStatus == 1)
  331.     WaitPktMask();
  332.  
  333.     if (ConsoleTaken) {
  334.     proc->pr_ConsoleTask = NULL;
  335.     ConsoleTaken = 0;
  336.     }
  337.     if (BreakMask) {
  338.     SetSignal(BreakMask, BreakMask);
  339.     chkabort();
  340.     BreakMask = 0;
  341.     }
  342.     return(ReturnCode);
  343. }
  344.  
  345. /*
  346.  *  Software Interrupt deals with handler functions
  347.  */
  348.  
  349. long
  350. _sys13SoftIntC()
  351. {
  352.     Message *msg;
  353.     DosPacket *pkt;
  354.  
  355.     while (msg = GetMsg(&PktPort)) {
  356.     pkt = (DosPacket *)msg->mn_Node.ln_Name;
  357.  
  358.     pkt->dp_Res2 = 0;
  359.  
  360.  
  361.     switch(pkt->dp_Type) {
  362.     case ACTION_READ:
  363.  
  364.         AddTail(&ReqList, &((Message *)pkt->dp_Link)->mn_Node);
  365.  
  366.         /*
  367.          *    Check command competion -- shell's cli_Module set to
  368.          *    NULL.  Also handle giving the shell a path
  369.          */
  370.  
  371.         {
  372.         Process *proc = pkt->dp_Port->mp_SigTask;
  373.  
  374.         if (proc->pr_Task.tc_Node.ln_Type == NT_PROCESS && proc->pr_CLI) {
  375.             CLI *cli = BTOC(proc->pr_CLI);
  376.  
  377.             if (cli->cli_Module == NULL) {
  378.             if (CmdStatus == 1) {
  379.                 CmdStatus = -1;
  380.                 ReturnCode = cli->cli_ReturnCode;
  381.             }
  382.             }
  383.         }
  384.         }
  385.         continue;
  386.     case ACTION_WRITE:
  387.         /*
  388.          *    Copy any output to our standard out (if we have a
  389.          *    standard out).    We have to skip the shell prompts
  390.          *    and other garbage which is why the junk.  Note that
  391.          *    we always force Res1 = Arg3 so the write 'succeeds'
  392.          *
  393.          *    we cannot write while running async (exceptions enabled)
  394.          *    as a crash would occur if we try to make a dos call
  395.          *    over a dos call in progress.
  396.          */
  397.  
  398.  
  399.         if (CmdStatus == 1 && TermFlag == 0) {
  400.         Process *proc = (Process *)PktPort.mp_Node.ln_Name;
  401.         CLI *remCli = BTOC(((Process *)pkt->dp_Port->mp_SigTask)->pr_CLI);
  402.  
  403.         if (remCli && remCli->cli_Module) {
  404.             if (proc->pr_Task.tc_Node.ln_Type == NT_PROCESS && proc->pr_COS) {
  405.             /*
  406.              *  forward the packet
  407.              */
  408.             PutMsg(((FileHandle *)BTOC(proc->pr_COS))->fh_Type, msg);
  409.             continue;
  410.             }
  411.         }
  412.         }
  413.         pkt->dp_Res1 = pkt->dp_Arg3;
  414.         break;
  415.     case ACTION_FINDUPDATE:
  416.     case ACTION_FINDINPUT:
  417.     case ACTION_FINDOUTPUT:
  418.         {
  419.         FileHandle *fh = BTOC(pkt->dp_Arg1);
  420.         fh->fh_Arg1 = pkt->dp_Arg1;
  421.         fh->fh_Port = (MsgPort *)DOS_TRUE;
  422.         pkt->dp_Res1 = DOS_TRUE;
  423.         }
  424.         ++DummyRefs;
  425.         break;
  426.     case ACTION_END:
  427.         pkt->dp_Res1 = DOS_TRUE;
  428.         if (--DummyRefs == 0) {    /*  shell shutdown */
  429.         if (CmdStatus == 1)
  430.             CmdStatus = -1;
  431.         ReturnCode = 0;
  432.         }
  433.         break;
  434.     default:
  435.         pkt->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
  436.         pkt->dp_Res1 = DOS_FALSE;
  437.         break;
  438.     }
  439.     ReturnPacket(pkt);
  440.     }
  441.     Signal((Task *)PktPort.mp_Node.ln_Name, PktMask);
  442.     return(0);
  443. }
  444.  
  445. /*
  446.  *  DEVICE CREATION AND DELETION
  447.  */
  448.  
  449.  
  450. void
  451. MkDevice(devName, port)
  452. char *devName;
  453. MsgPort *port;
  454. {
  455.     DosList *dl;
  456.     RootNode *root;
  457.     DosInfo *info;
  458.  
  459.     Dl = dl = (struct DosList *)DosAllocMem(sizeof(struct DosList)+strlen(devName)+2);
  460.     strcpy((char *)(dl+1) + 1, devName);
  461.     *(char *)(dl + 1) = strlen(devName);
  462.     dl->dol_Type = DLT_DEVICE;
  463.     dl->dol_Task = port;
  464.     dl->dol_Name = MKBADDR((char *)(dl+1));
  465.  
  466.     Forbid();
  467.     root  = (struct RootNode *)DOSBase->dl_Root;
  468.     info  = (struct DosInfo  *)BADDR(root->rn_Info);
  469.     dl->dol_Next = info->di_DevInfo;
  470.     info->di_DevInfo = MKBADDR(dl);
  471.     Permit();
  472. }
  473.  
  474. void
  475. DelDevice()
  476. {
  477.     DosList *dl;
  478.     DosInfo *info;
  479.     RootNode *root;
  480.     DosList *dls;
  481.     BPTR    *bpp;
  482.  
  483.     if (dl = Dl) {
  484.     Forbid();
  485.     root  = (struct RootNode *)DOSBase->dl_Root;
  486.     info  = (struct DosInfo  *)BADDR(root->rn_Info);
  487.  
  488.     for (bpp = &info->di_DevInfo; dls = BADDR(*bpp); bpp = &dls->dol_Next) {
  489.         if (dls == dl)
  490.         break;
  491.     }
  492.     if (dls == dl) {
  493.         *bpp = dls->dol_Next;
  494.     } else {
  495.         Alert(0x07AAAAAA|AT_Recovery);
  496.     }
  497.     Permit();
  498.     DosFree(dl);
  499.     Dl = NULL;
  500.     }
  501. }
  502.  
  503. void *
  504. DosAllocMem(bytes)
  505. long bytes;
  506. {
  507.     long *ptr;
  508.  
  509.     bytes += 4;
  510.  
  511.     if (ptr = AllocMem(bytes, MEMF_PUBLIC | MEMF_CLEAR)) {
  512.     *ptr++ = bytes;
  513.     return((void *)ptr);
  514.     }
  515.     Alert(AG_NoMemory|AT_DeadEnd);
  516. }
  517.  
  518. void
  519. DosFree(vptr)
  520. void *vptr;
  521. {
  522.     long *ptr = vptr;
  523.     --ptr;
  524.     FreeMem(ptr, *ptr);
  525. }
  526.  
  527. void
  528. ReturnPacket(packet)
  529. DosPacket *packet;
  530. {
  531.     Message *mess;
  532.     MsgPort *replyPort;
  533.  
  534.     replyPort             = packet->dp_Port;
  535.     mess             = packet->dp_Link;
  536.     packet->dp_Port         = &PktPort;
  537.     mess->mn_Node.ln_Name    = (char *)packet;
  538.     PutMsg(replyPort, mess);
  539. }
  540.  
  541. /*
  542.  *  Copy the path from the workbench process's CLI into the specified CLI
  543.  */
  544.  
  545. void
  546. CopyWorkbenchPath(dcli)
  547. CLI *dcli;
  548. {
  549.     CLI *scli;
  550.     LockList *lls;
  551.     Process *sproc;
  552.  
  553.     if ((sproc = (struct Process *)FindTask("Workbench")) && sproc->pr_Task.tc_Node.ln_Type == NT_PROCESS) {
  554.     if (scli = BTOC(sproc->pr_CLI)) {
  555.         for (lls = BTOC(scli->cli_CommandDir); lls; lls = BTOC(lls->NextPath)) {
  556.         BPTR lock;
  557.         LockList *ll;
  558.         LockList **llast = (LockList **)&dcli->cli_CommandDir;
  559.  
  560.         if (lock = DupLock(lls->PathLock)) {
  561.             if (ll = ((LockList *)AllocMem(sizeof(LockList) + 4, MEMF_PUBLIC|MEMF_CLEAR) + 1)) {
  562.             ((long *)ll)[-1] = sizeof(LockList) + 4;
  563.             ll->NextPath = (BPTR)llast;
  564.             ll->PathLock = lock;
  565.             *llast = (LockList *)MKBADDR(ll);
  566.             llast = (LockList **)&ll->NextPath;
  567.             } else {
  568.             UnLock(lock);
  569.             }
  570.         }
  571.         }
  572.     }
  573.     }
  574. }
  575.  
  576. void
  577. WaitPktMask(void)
  578. {
  579.     long mask = 0;
  580.  
  581.     while ((mask & PktMask) == 0) {
  582.     mask = Wait(PktMask | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F);
  583.     if (mask & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F)) {
  584.         if (RemShellTask)
  585.         Signal(RemShellTask, (mask & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F)));
  586.         else
  587.         BreakMask |= mask & (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F);
  588.     }
  589.     }
  590. }
  591.  
  592.  
  593.  
  594.